package shake.servlet;

import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import shake.Shake;
import shake.annotation.ApplicationScoepd;
import shake.context.ComponentRegistry;
import shake.event.EventAgent;
import shake.filter.HotDeployFilter;
import shake.init.module.ComponentDescriptor;
import shake.init.module.ServletContextModule;

import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;
import com.google.inject.servlet.SessionScoped;

public class ShakeListener implements ServletContextListener,
		HttpSessionListener {

	public void contextDestroyed(ServletContextEvent arg0) {
		Injector injector = ShakeServletContext.getInjector();
		destoryApplicationScoped(arg0.getServletContext(), injector);
	}

	static private void destoryApplicationScoped(ServletContext context,
			Injector injector) {
		ComponentRegistry reg = injector.getInstance(ComponentRegistry.class);
		EventAgent invoke = injector.getInstance(EventAgent.class);
		for (ComponentDescriptor desc : reg) {
			if (ApplicationScoepd.class.equals(desc.getScopeAnnotation())) {
				Object obj = context.getAttribute(desc.getKey().toString());
				if (obj != null) {
					invoke.getDestory().fireEvent(obj);
					context.removeAttribute(desc.getKey().toString());
				}

			}
		}
	}

	public void contextInitialized(ServletContextEvent arg0) {
		ShakeServletContext.setContext(arg0.getServletContext());
		createInjector(arg0.getServletContext());
	}

	static public ClassLoader hotdeployClassLoader(ServletContext servletContext) {
		try {
			ClassLoader loader = Thread.currentThread().getContextClassLoader();
			URL r = servletContext.getResource("/WEB-INF/dev/");
			URLClassLoader loader2 = new URLClassLoader(new URL[] { r }, loader);
			return loader2;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	static public void createInjector(ServletContext servletContext) {
		Injector injector = ShakeServletContext.getInjector();
		if (injector != null) {
			destoryApplicationScoped(servletContext, injector);
		}
		ClassLoader hotDeployClassLoader = hotdeployClassLoader(servletContext);
		List<Module> m = new LinkedList<Module>();
		m.add(new ServletContextModule(servletContext));

		injector = Shake.create(Stage.DEVELOPMENT, m, hotDeployClassLoader);
		ShakeServletContext.setHotDeployClassLoader(hotDeployClassLoader);
		ShakeServletContext.setInjector(injector);
		servletContext.setAttribute(HotDeployFilter.LAST_MODIFY_TIME, System
				.currentTimeMillis());
		startup(injector, ApplicationScoepd.class);
		EventAgent e = injector.getInstance(EventAgent.class);
		e.getPostInstallEvent().fireEvent();
	}

	static private void startup(Injector injector,
			Class<? extends Annotation> scopeAnnotation) {
		ComponentRegistry reg = injector.getInstance(ComponentRegistry.class);
		EventAgent invoke = injector.getInstance(EventAgent.class);
		for (ComponentDescriptor desc : reg) {
			Class<?> clazz = desc.getComponentClass();
			if (desc.isStartup()
					&& scopeAnnotation.equals(desc.getScopeAnnotation())) {
				Object obj = injector.getInstance(desc.getKey());
				invoke.getStartup().fireEvent(obj);
			}

		}
	}

	private void destorySession(HttpSessionEvent event, Injector injector,
			Class<? extends Annotation> scopeAnnotation) {
		ComponentRegistry reg = injector.getInstance(ComponentRegistry.class);
		EventAgent invoke = injector.getInstance(EventAgent.class);
		HttpSession session = event.getSession();
		Enumeration<String> names = session.getAttributeNames();

		for (ComponentDescriptor desc : reg) {
			if (!SessionScoped.class.equals(desc.getScopeAnnotation())) {
				continue;
			}
			Object obj = session.getAttribute(desc.getKey().toString());
			if (obj == null) {
				continue;
			}
			invoke.getDestory().fireEvent(obj);
		}

	}

	public void sessionCreated(HttpSessionEvent se) {
		Injector injector = ShakeServletContext.getInjector();
		startup(injector, SessionScoped.class);

	}

	public void sessionDestroyed(HttpSessionEvent se) {

	}

}
